// 整体功能
// 该代码实现了一个基于点对点网络的聊天应用，结合了多种协议和功能，
// 支持局域网内节点自动发现、分布式节点发现和存储、消息的可靠传输以及节点身份验证等功能。
// 如何运行
// 先启动一个节点
// export CHAT_P2P_MDNS_ENABLE=false && cargo run --example p2p_chat_with_dht
// 启动第二个节点, 需要指定第一个节点的端口和 peer_id
// export CHAT_P2P_MDNS_ENABLE=false && export CHAT_P2P_BOOTSTRAP_PEERS=/ip4/127.0.0.1/tcp/{port}/p2p/{peer_id} && cargo run --example p2p_chat_with_dht
// 启动第三个节点, 需要指定第一个节点的端口和 peer_id ...
// 其他节点以此类推
use std::time::Duration;

// 引入anyhow的错误处理库，用于简化错误处理
#[allow(unused)]
use anyhow::{Context, anyhow};

// 引入libp2p相关模块
use libp2p::{
    Multiaddr, StreamProtocol, SwarmBuilder,
    futures::StreamExt,
    identify,
    kad::{self, Mode, store::MemoryStore},
    mdns,
    multiaddr::Protocol,
    noise, ping,
    request_response::{self, json},
    swarm::{NetworkBehaviour, SwarmEvent, behaviour::toggle},
    tcp, yamux,
};
use serde::{Deserialize, Serialize}; // 序列化/反序列化支持
use tokio::io::{self, AsyncBufReadExt, BufReader}; // 异步IO操作

// 定义消息请求结构体，用于发送聊天消息
#[derive(Debug, Clone, Serialize, Deserialize)]
struct MessageRequest {
    pub message: String, // 消息内容字段，使用String类型存储文本
}

// 定义消息响应结构体，用于确认消息接收
#[derive(Debug, Clone, Serialize, Deserialize)]
struct MessageResponse {
    pub ack: bool, // 确认标志，true表示成功接收
}

// 定义网络行为组合，包含三种协议行为
#[derive(NetworkBehaviour)]
struct ChatBehavior {
    ping: ping::Behaviour, // Ping行为：定期发送Ping保持连接
    messaging: json::Behaviour<MessageRequest, MessageResponse>, // 消息行为：处理JSON格式的聊天消息
    mdns: toggle::Toggle<mdns::Behaviour<mdns::tokio::Tokio>>, // mDNS行为：用于发现和连接其他节点, 包了一层Toggle，用于动态启用或禁用mDNS
    identify: identify::Behaviour,                             // 身份验证行为：用于验证节点身份
    // 使用 Kademlia 分布式哈希表协议进行节点发现和存储，设置了引导节点和定期引导间隔。
    // 好处：实现了分布式节点发现，即使在广域网环境下，节点也能通过引导节点快速加入网络。
    // 坏处：可能会增加网络负载，因为需要定期发送查询和响应。
    // 解决方法：可以通过设置定期引导间隔来减少网络负载，但是也可能会增加网络延迟。
    kademlia: kad::Behaviour<MemoryStore>, // Kademlia协议：用于分布式节点发现和存储
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mdns_enable: bool = std::env::var("CHAT_P2P_MDNS_ENABLE")?.parse()?;
    let bootstrap_peers = std::env::var("CHAT_P2P_BOOTSTRAP_PEERS").map(|peers| {
        peers
            .split(",")
            .map(|peer| peer.to_string())
            .collect::<Vec<String>>()
    });

    // 构建Swarm实例 - P2P网络的核心管理结构
    let mut swarm = SwarmBuilder::with_new_identity() // 创建新身份(自动生成密钥对)
        .with_tokio() // 使用tokio异步运行时
        .with_tcp(
            // 配置TCP传输层
            tcp::Config::default(), // 默认TCP配置
            noise::Config::new,     // Noise加密配置
            yamux::Config::default, // Yamux多路复用配置
        )?
        .with_behaviour(|key_pair| {
            let mdns = if mdns_enable {
                // 启用mDNS功能
                toggle::Toggle::from(Some(mdns::Behaviour::new(
                    mdns::Config::default(),        // 默认mDNS配置
                    key_pair.public().to_peer_id(), // 使用节点的公钥生成Peer ID
                )?))
            } else {
                // 如果禁用mDNS，创建一个空的Toggle
                toggle::Toggle::from(None)
            };
            let peer_id = key_pair.public().to_peer_id();
            let mut kad_config = kad::Config::new(StreamProtocol::new("/awesome-chat/kad/1.0.0"));
            kad_config.set_periodic_bootstrap_interval(Some(Duration::from_secs(10)));

            // 配置网络行为
            Ok(ChatBehavior {
                ping: ping::Behaviour::new(
                    // 初始化Ping行为
                    ping::Config::new() // 每10秒发送一次Ping
                        .with_interval(Duration::from_secs(10)),
                ),
                messaging: json::Behaviour::new(
                    // 初始化消息行为
                    [(
                        // 定义自定义协议"/awesome-chat/1"
                        StreamProtocol::new("/awesome-chat/1"),
                        // 支持完整的请求-响应模式
                        request_response::ProtocolSupport::Full,
                    )],
                    request_response::Config::default(), // 使用默认配置
                ),
                // 初始化mDNS行为，用于局域网节点发现
                mdns,
                // 初始化身份验证行为
                identify: identify::Behaviour::new(identify::Config::new(
                    "1.0.0".to_string(),
                    key_pair.public(),
                )),
                // 初始化Kademlia协议行为，用于分布式节点发现和存储
                kademlia: kad::Behaviour::with_config(
                    peer_id,
                    MemoryStore::new(peer_id),
                    kad_config,
                ),
            })
        })?
        // 配置Swarm参数：30秒空闲连接超时
        .with_swarm_config(|config| config.with_idle_connection_timeout(Duration::from_secs(30)))
        .build(); // 完成构建

    // 开始监听随机可用TCP端口(0表示由操作系统自动分配)
    swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
    swarm.behaviour_mut().kademlia.set_mode(Some(Mode::Server));

    // 打印本地节点ID
    println!("Peer ID: {:?}", swarm.local_peer_id());

    if let Ok(bootstrap_peers) = bootstrap_peers {
        for bootstrap_peer in bootstrap_peers {
            let addr: Multiaddr = bootstrap_peer.parse().unwrap();
            let peer_id = addr
                .iter()
                .map(|_addr_str| {
                    if let Some(Protocol::P2p(peer_id)) = addr.iter().last() {
                        return Some(peer_id);
                    }
                    None
                })
                .filter(Option::is_some)
                .last()
                .ok_or(anyhow!("No peer id found in address"))?
                .ok_or(anyhow!("Bootstrap peer address {bootstrap_peer} is wrong"))?;
            swarm.behaviour_mut().kademlia.add_address(&peer_id, addr);
        }
    }

    // 准备从标准输入异步读取消息
    let mut stdin = BufReader::new(io::stdin()).lines();

    // 主事件循环
    loop {
        tokio::select! {  // 同时监听网络事件和用户输入
            // 处理网络事件
            event = swarm.select_next_some() => match event {
                // 新的监听地址事件
                SwarmEvent::NewListenAddr { address, .. } => {
                    println!("Listening on {:?}", address);
                }
                // 连接建立成功事件
                SwarmEvent::ConnectionEstablished { peer_id, .. } => {
                    println!("Established connection to peer: {:?}", peer_id);
                },
                // 网络行为事件
                SwarmEvent::Behaviour(event) => match event {
                    // Ping协议事件
                    ChatBehaviorEvent::Ping(event) => {
                        println!("Received ping: {:?}", event);
                    }
                    // 消息协议事件
                    ChatBehaviorEvent::Messaging(event) => match event {
                        request_response::Event::Message {
                            peer, connection_id, message
                        } => match message {
                            // 处理接收到的消息请求
                            request_response::Message::Request { request_id, request, channel } => {
                                println!("Received request from peer: {:?}, connection_id: {:?}, request_id: {:?}, request: {:?}",
                                    peer, connection_id, request_id, request);
                                // 构造确认响应
                                let response = MessageResponse { ack: true };
                                // 发送响应
                                if let Err(e) = swarm
                                    .behaviour_mut()
                                    .messaging
                                    .send_response(channel, response) {
                                        println!("Error sending response: {:?}", e);
                                    }
                            },
                            // 处理接收到的消息响应
                            request_response::Message::Response { request_id, response,.. } => {
                                println!("Received response from peer: {:?}, connection_id: {:?}, request_id: {:?}, response: {:?}",
                                    peer, connection_id, request_id, response);
                            }
                        },
                        // 其他消息协议事件(已简化)
                        _ => {}
                    },
                    // mDNS服务发现事件
                    ChatBehaviorEvent::Mdns(event) => match event {
                        // 发现新节点事件
                        mdns::Event::Discovered(new_peers) => {
                            // 遍历所有发现的节点
                            for (peer_id, addr) in new_peers {
                                println!("Discovered peer: {:?} at {:?}", peer_id, addr);
                                // 尝试连接发现的节点
                                if let Err(e) = swarm.dial(peer_id) {
                                    println!("Error dialing peer: {:?}", e);
                                    continue;
                                }
                                // 添加节点地址到Swarm
                                swarm.add_peer_address(peer_id, addr);
                            }
                        }
                        // 节点过期事件(当前不做处理)
                        mdns::Event::Expired(_) => {}
                    },
                    // 身份验证事件
                    ChatBehaviorEvent::Identify(event) => match event {
                        // 节点身份验证成功事件
                        identify::Event::Received { connection_id, peer_id, info } => {
                            println!("Received identify from {:?} ({:?}): {:?}", peer_id, connection_id, info);
                            for addr in info.listen_addrs {
                                swarm.behaviour_mut().kademlia.add_address(&peer_id, addr);
                            }
                        },
                        // 身份验证发送事件
                        identify::Event::Sent { connection_id, peer_id } => {
                            println!("Sent identify to {:?} ({:?})", peer_id, connection_id);
                        }
                        // 身份验证推送事件
                        identify::Event::Pushed { connection_id, peer_id, info } => {
                            println!("Pushed identify to {:?} ({:?}): {:?}", peer_id, connection_id, info);
                        }
                        // 身份验证错误事件
                        identify::Event::Error { connection_id, peer_id, error } => {
                            println!("Error: {:?} from {:?} ({:?})", error, peer_id, connection_id);
                        }
                    },
                    // Kademlia协议事件
                    ChatBehaviorEvent::Kademlia(event) => match event {
                        kad::Event::InboundRequest { .. } => {},
                        kad::Event::OutboundQueryProgressed {..} => {},
                        kad::Event::RoutingUpdated { peer, is_new_peer, addresses, bucket_range, old_peer } => {
                            println!("Routing updated: peer: {:?}, is_new_peer: {:?}, addresses: {:?}, bucket_range: {:?}, old_peer: {:?}", peer, is_new_peer, addresses, bucket_range, old_peer);
                            addresses.iter().for_each(|addr| {
                                if let Err(e) = swarm.dial(addr.clone()) {
                                    println!("Error dialing peer: {:?}", e);
                                }
                            })
                        },
                        kad::Event::UnroutablePeer {..} => {},
                        kad::Event::RoutablePeer {..} => {},
                        kad::Event::PendingRoutablePeer {..} => {},
                        kad::Event::ModeChanged {..} => {},
                    }
                }
                // 其他网络事件(已简化)
                _ => {}
            },

            // 处理用户输入
            Ok(Some(line)) = stdin.next_line() => {
                // 获取所有已连接的节点ID
                let peer_ids = swarm.connected_peers().copied().collect::<Vec<_>>();
                // 向所有连接节点发送消息
                for peer_id in peer_ids {
                    let message = MessageRequest { message: line.clone() };
                    // 发送消息请求
                    let _ = swarm
                        .behaviour_mut()
                        .messaging
                        .send_request(&peer_id, message);
                    // 打印本地发送的消息
                    println!("{}: {:?}", swarm.local_peer_id(), line);
                }
            }
        }
    }
}
